﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Cms.Domain
{
    [Serializable]
    public class Page : ContentSetContainer, IAuditable, IComparable<Page>
    {
        private IList<VisitedPublicUrl> _adminVisits;
        private IList<AdministratorGroup> _allowedAdministratorGroups;
        private string _bodyBottomHtml;
        private string _bodyTopHtml;
        private IList<Category> _categories;
        private IList<Comment> _comments;
        private IList<PageContentLink> _content;
        private IList<AdministratorGroup> _deniedAdministratorGroups;
        private IList<UserGroup> _deselectedUserGroups;
        private DetailTemplate _detailTemplate;
        private DateTime? _endPublishing;
        private string _filename;
        private Folder _folder;
        private string _headHtml;
        private int _hits;
        private IDictionary<int, PageLocalizedProperties> _localizedProperties;
        private MasterTemplate _masterTemplate;
        private IList<MenuItem> _menuItems;
        private IList<UserGroup> _requiredUserGroups;
        private string _sortKey;
        private DateTime? _startPublishing;

        public Page()
        {
            _adminVisits = new List<VisitedPublicUrl>();
            _categories = new List<Category>();
            _localizedProperties = new Dictionary<int, PageLocalizedProperties>();
            _content = new List<PageContentLink>();
            _menuItems = new List<MenuItem>();
            _hits = 0;
            _comments = new List<Comment>();
            _allowedAdministratorGroups = new List<AdministratorGroup>();
            _deniedAdministratorGroups = new List<AdministratorGroup>();
            _requiredUserGroups = new List<UserGroup>();
            _deselectedUserGroups = new List<UserGroup>();
        }
        
        public virtual string Filename
        {
            get { return _filename; }
            set { _filename = value; }
        }

        public virtual Folder Folder
        {
            get { return _folder; }
            set { _folder = value; }
        }
        
        public virtual MasterTemplate MasterTemplate
        {
            get { return _masterTemplate; }
            set { _masterTemplate = value; }
        }
        
        public virtual DetailTemplate DetailTemplate
        {
            get { return _detailTemplate; }
            set { _detailTemplate = value; }
        }
        
        public virtual DateTime? StartPublishing
        {
            get { return _startPublishing; }
            set { _startPublishing = value; }
        }

        public virtual DateTime? EndPublishing
        {
            get { return _endPublishing; }
            set { _endPublishing = value; }
        }
        
        public virtual string HeadHtml
        {
            get { return _headHtml; }
            set { _headHtml = value; }
        }
        
        public virtual string BodyTopHtml
        {
            get { return _bodyTopHtml; }
            set { _bodyTopHtml = value; }
        }
        
        public virtual string BodyBottomHtml
        {
            get { return _bodyBottomHtml; }
            set { _bodyBottomHtml = value; }
        }
        
        public virtual IList<VisitedPublicUrl> AdminVisits
        {
            get { return _adminVisits; }
            set { _adminVisits = value; }
        }
        
        public virtual IList<Category> Categories
        {
            get { return _categories; }
            set { _categories = value; }
        }

        public virtual string SortKey
        {
            get { return _sortKey; }
            set { _sortKey = value; }
        }
        
        public virtual IDictionary<int, PageLocalizedProperties> LocalizedProperties
        {
            get { return _localizedProperties; }
            set { _localizedProperties = value; }
        }
        
        public virtual IList<PageContentLink> Content
        {
            get { return _content; }
            set { _content = value; }
        }

        public virtual IList<MenuItem> MenuItems
        {
            get { return _menuItems; }
            set { _menuItems = value; }
        }
        
        public virtual int Hits
        {
            get { return _hits; }
            set { _hits = value; }
        }

        public virtual IList<Comment> Comments
        {
            get { return _comments; }
            set { _comments = value; }
        }
        
        public virtual IList<AdministratorGroup> AllowedAdministratorGroups
        {
            get { return _allowedAdministratorGroups; }
            set { _allowedAdministratorGroups = value; }
        }
        
        public virtual IList<AdministratorGroup> DeniedAdministratorGroups
        {
            get { return _deniedAdministratorGroups; }
            set { _deniedAdministratorGroups = value; }
        }

        public virtual IList<UserGroup> RequiredUserGroups
        {
            get { return _requiredUserGroups; }
            set { _requiredUserGroups = value; }
        }
        
        public virtual IList<UserGroup> DeselectedUserGroups
        {
            get { return _deselectedUserGroups; }
            set { _deselectedUserGroups = value; }
        }

        #region IAuditable Members

        public virtual string GetState()
        {
            return "Name = " + Name +
                   ", Filename = " + _filename +
                   ", FolderID = " + (_folder != null ? _folder.ID.ToString() : "NULL") +
                   ", Path = " + GetVirtualPath();
        }

        #endregion

        public override string GetPublishedContentHtml(string areaID, int localeID)
        {
            foreach (PageContentLink pl in _content.Where(pl => pl.AreaID == areaID))
            {
                return pl.Content.GetPublishedHtml(localeID);
            }
            return string.Empty;
        }
        
        public override string GetPublishedContentTitle(string areaID, int localeID)
        {
            foreach (PageContentLink pl in _content.Where(pl => pl.AreaID == areaID))
            {
                return pl.Content.GetPublishedTitle(localeID);
            }
            return string.Empty;
        }
        
        public override string GetContentHtml(string areaID, int localeID, string timestamp)
        {
            foreach (PageContentLink pl in _content.Where(pl => pl.AreaID == areaID))
            {
                return pl.Content.GetHtml(localeID, timestamp);
            }
            return string.Empty;
        }
        
        public override string GetContentTitle(string areaID, int localeID, string timestamp)
        {
            foreach (PageContentLink pl in _content.Where(pl => pl.AreaID == areaID))
            {
                return pl.Content.GetTitle(localeID, timestamp);
            }
            return string.Empty;
        }
        
        public virtual string GetVirtualPath()
        {
            IList<string> path = new List<string>();
            Folder f = _folder;

            // Create the path backwards, starting with the pagename and up through the folder structure
            path.Add(_filename);
            while (f != null)
            {
                path.Add(f.Name);
                f = f.Parent;
            }
            string[] pathArray = path.ToArray();
            Array.Reverse(pathArray);
            return string.Join("/", pathArray);
        }
        
        public override Content GetContent(string areaID)
        {
            return (from pl in _content where pl.AreaID == areaID select pl.Content).FirstOrDefault();
        }
        
        public override void AddContentLink(string areaID, Content content)
        {
            IList<PageContentLink> links = _content.Where(existingLink => existingLink.AreaID == areaID).ToList();
            foreach (PageContentLink existingLink in links)
            {
                // Remove all versions associated with the old content from the sets
                foreach (var kvp in ContentSets)
                {
                    ContentSet set = kvp.Value;
                    IList<ContentTranslationVersion> removableVersions = new List<ContentTranslationVersion>();
                    foreach (ContentTranslationVersion version in set.TranslationVersions)
                    {
                        if (version.Translation.Content.ID == existingLink.Content.ID)
                            removableVersions.Add(version);
                    }
                    foreach (ContentTranslationVersion version in removableVersions)
                        set.TranslationVersions.Remove(version);
                }
                _content.Remove(existingLink);
            }
            var link = new PageContentLink
                           {
                               Content = content,
                               AreaID = areaID,
                               Page = this
                           };
            _content.Add(link);
            content.Pages.Add(link);
        }
        
        public override void PublishContentVersion(int localeID, ContentTranslationVersion ctv)
        {
            if (ContentSets.ContainsKey(localeID))
                ContentSets[localeID].PublishedVersion = ctv;
            foreach (ContentTranslation translation in from pageLink in _content
                                                       where pageLink.Content.Translations.ContainsKey(localeID)
                                                       select pageLink.Content.Translations[localeID])
            {
                translation.PublishVersion(ctv.Timestamp);
            }
        }
        
        public virtual bool IsInCategory(string name)
        {
            foreach (Category category in _categories)
                if (category.Name.ToLower() == name.ToLower())
                    return true;
            if (_folder != null)
                return _folder.IsInCategory(name);
            return false;
        }
        
        public virtual IList<Category> GetAllCategories()
        {
            IList<Category> categories = new List<Category>();
            foreach (Category c in _categories)
                categories.Add(c);
            if (_folder != null)
                foreach (Category c in _folder.GetAllCategories())
                    if (!categories.Contains(c))
                        categories.Add(c);
            return categories;
        }
        
        public virtual string[] GetCategoryNames()
        {
            return GetAllCategories().Select(pc => pc.Name).ToArray();
        }
        
        public virtual IList<UserGroup> GetRequiredUserGroups()
        {
            IList<UserGroup> groups = new List<UserGroup>();
            foreach (UserGroup group in _requiredUserGroups)
                groups.Add(group);
            if (_folder != null)
                foreach (UserGroup group in _folder.GetRequiredUserGroups())
                    if (!groups.Contains(group))
                        groups.Add(group);
            foreach (UserGroup group in _deselectedUserGroups)
                if (groups.Contains(group))
                    groups.Remove(group);
            return groups;
        }
        
        public virtual bool IsPublishedNow()
        {
            return
                !((_startPublishing.HasValue && _startPublishing.Value > DateTime.Now) ||
                  (_endPublishing.HasValue && _endPublishing.Value < DateTime.Now));
        }

        public virtual DateTime? GetPublishedTimestamp(int localeID)
        {
            if (!ContentSets.ContainsKey(localeID) || ContentSets[localeID].PublishedVersion == null)
            {
                return null;
            }
            return new DateTime(long.Parse(ContentSets[localeID].PublishedVersion.Timestamp));
        }

        public virtual string GetPublishedBy(int localeID)
        {
            if (!ContentSets.ContainsKey(localeID) || ContentSets[localeID].PublishedVersion == null)
            {
                return null;
            }
            return ContentSets[localeID].PublishedVersion.Author.Fullname;
        }

        public virtual int CompareTo(Page other)
        {
            return ID > 0 && ID == other.ID ? 0 : 1;
        }

        public virtual bool AllowsAdministrator(Administrator administrator)
        {
            if (_deniedAdministratorGroups.Contains(administrator.Group))
            {
                return false;
            }
            if (_allowedAdministratorGroups.Contains(administrator.Group))
            {
                return true;
            }
            if (_folder != null)
            {
                if (_folder.GetAllDeniedAdministratorGroups().Contains(administrator.Group))
                {
                    return false;
                }
                if (_folder.GetAllAllowedAdministratorGroups().Contains(administrator.Group))
                {
                    return true;
                }
            }
            return administrator.Group.DefaultPageAccess == DefaultAccess.Allow;
        }

        protected override ContentSet CreateContentSet(Locale locale)
        {
            return new PageContentSet(this, locale);
        }
    }
}